Go 语言的并发编程是其最大的特色之一,主要通过 goroutines
和 channels
来实现。下面我将介绍这两个概念及其它相关的并发机制
一、Goroutines
goroutine
是 Go 语言中实现并发的基本单位。它是轻量级的线程,由 Go 运行时管理。
创建 Goroutine 只需在函数调用前加上 go
关键字,即可新开一个 goroutine。
go
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
go say("world")
time.Sleep(time.Second)
}
二、 Channels
channel
是用来在 goroutines 之间传递消息的管道。可以通过它发送和接收不同类型的值,从而实现数据的同步交流。
创建和使用 Channel
go
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 将 sum 发送到 channel c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从 channel c 接收
fmt.Println(x, y, x+y)
}
三、Select
select
语句用于在多个 channel 操作中进行选择。它类似于 switch 语句,但是每个 case 都是一个通信操作。
使用 Select
go
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
四、Sync
sync
包提供了基本的同步原语,如互斥锁(Mutex
)和等待组(WaitGroup
)。
使用 WaitGroup
go
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
}
Go 的并发模型被设计得简单而强大,旨在使并发编程更加容易和安全。通过 goroutines
, channels
, select
, 和 sync
等机制,可以构建高效且易于维护的并发程序。